<template>
  <el-date-picker v-bind="attrs" :value="date" v-on="$listeners" />
</template>

<script>
import { excludeKeys } from '@/utils/index/';
import { addDays, subDays } from 'date-fns';
import { parseDate } from 'element-ui/src/utils/date-util';

function getPickerShortcuts(instance) {
  return [
    {
      key: 'today',
      text: instance.$t('today'),
      onClick: picker => {
        const start = new Date();
        picker.$emit('pick', [start, start]);
      }
    },
    {
      key: 'yesterday',
      text: instance.$t('yesterday'),
      onClick: picker => {
        const start = new Date();
        start.setTime(start.getTime() - 3600 * 1000 * 24 * 1);
        picker.$emit('pick', [start, start]);
      }
    },
    {
      key: 'this week',
      text: instance.$t('this_week'), // 从周一算起
      onClick: picker => {
        const end = new Date();
        const start = new Date();
        const days = new Date().getDay() == 0 ? 6 : new Date().getDay() - 1;
        start.setTime(start.getTime() - 3600 * 1000 * 24 * days);
        picker.$emit('pick', [start, end]);
      }
    },
    {
      key: 'this month',
      text: instance.$t('this_month'),
      onClick: picker => {
        const end = new Date();
        const start = new Date();
        const days = new Date().getDate() - 1;
        start.setTime(start.getTime() - 3600 * 1000 * 24 * days);
        picker.$emit('pick', [start, end]);
      }
    },
    {
      key: 'last 7 days',
      text: instance.$tt('last_N_days', { N: 7 }),
      onClick: picker => {
        const end = new Date();
        const start = new Date();
        start.setTime(start.getTime() - 3600 * 1000 * 24 * 6);
        picker.$emit('pick', [start, end]);
      }
    },
    {
      key: 'last 15 days',
      text: instance.$tt('last_N_days', { N: 15 }),
      onClick(picker) {
        const end = new Date();
        const start = new Date();
        start.setTime(start.getTime() - 3600 * 1000 * 24 * 14);
        picker.$emit('pick', [start, end]);
      }
    },
    {
      key: 'past 7 days',
      text: instance.$tt('past_N_days', { N: 7 }), // 过去7天 不包括今天 共7天
      onClick: picker => {
        const end = new Date();
        const start = new Date();
        start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
        end.setTime(end.getTime() - 3600 * 1000 * 24 * 1);
        picker.$emit('pick', [start, end]);
      }
    },
    {
      key: 'last 30 days',
      text: instance.$tt('last_N_days', { N: 30 }),
      onClick: picker => {
        const end = new Date();
        const start = new Date();
        start.setTime(start.getTime() - 3600 * 1000 * 24 * 29);
        picker.$emit('pick', [start, end]);
      }
    },
    {
      key: 'past 30 days',
      text: instance.$tt('past_N_days', { N: 30 }), // 过去30天 不包括今天 共30天
      onClick: picker => {
        const end = new Date();
        const start = new Date();
        start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
        end.setTime(end.getTime() - 3600 * 1000 * 24 * 1);
        picker.$emit('pick', [start, end]);
      }
    },
    {
      key: 'last 6 weeks',
      text: instance.$tt('last_N_weeks', { N: 6 }),
      onClick: picker => {
        const end = new Date();
        const start = new Date();
        const days = new Date().getDay() - 1 + 35;
        start.setTime(start.getTime() - 3600 * 1000 * 24 * days);
        picker.$emit('pick', [start, end]);
      }
    },
    {
      key: 'past 6 weeks',
      text: instance.$tt('past_N_weeks', { N: 6 }), // 过去6个完整周 不包括本周
      onClick: picker => {
        const end = new Date();
        const start = new Date();
        const days = new Date().getDay() == 0 ? 7 : new Date().getDay();
        start.setTime(start.getTime() - 3600 * 1000 * 24 * (days + 41));
        end.setTime(end.getTime() - 3600 * 1000 * 24 * days);
        picker.$emit('pick', [start, end]);
      }
    },
    {
      key: 'past 6 months',
      text: instance.$tt('past_N_months', { N: 6 }), // 前6月  不包括本月
      onClick: picker => {
        const start = new Date(
          new Date().getFullYear(),
          new Date().getMonth() - 6,
          1
        );
        const days = new Date(
          new Date().getFullYear(),
          new Date().getMonth(),
          0
        ).getDate();
        const end = new Date(
          new Date().getFullYear(),
          new Date().getMonth() - 1,
          days
        );
        picker.$emit('pick', [start, end]);
      }
    },
    {
      key: 'last 6 months',
      text: instance.$tt('last_N_months', { N: 6 }),
      onClick: picker => {
        const end = new Date();
        const start = new Date(
          new Date().getFullYear(),
          new Date().getMonth() - 5,
          1
        );
        picker.$emit('pick', [start, end]);
      }
    }
  ];
}

const SHORTCUT_SETS = {
  A: [
    'today',
    'yesterday',
    'this week',
    'this month',
    'last 7 days',
    'last 30 days',
    'last 6 weeks',
    'last 6 months'
  ],
  B: [
    'today',
    'yesterday',
    'this week',
    'this month',
    'past 7 days',
    'past 30 days',
    'past 6 weeks',
    'past 6 months'
  ],
  C: ['yesterday', 'this week', 'this month', 'past 7 days', 'past 30 days'],
  D: ['this week', 'this month', 'last 7 days', 'last 15 days', 'last 30 days'],
  E: ['last 7 days', 'last 15 days', 'last 30 days']
};

function normalizeShortcutKey(str) {
  return `${str}`
    .split(/\s/)
    .filter(t => t)
    .map(t => t.toLowerCase())
    .join(' ');
}

function filterShortcusts(list, keys) {
  const normalizedKeys = keys.map(normalizeShortcutKey);
  const isKeyExist = t =>
    normalizedKeys.some(k => k === normalizeShortcutKey(t.key));
  return list.filter(isKeyExist);
}

export default {
  props: {
    value: {
      required: true,
      validator(value) {
        const validate = v => {
          if (v == null) return true;
          if (v instanceof Date) return true;
          if (typeof v === 'string') return true;
          if (Array.isArray(v)) return v.every(validate);
          return false;
        };
        return validate(value);
      }
    },
    type: {
      required: true,
      validator(value) {
        return ['daterange', 'datetimerange'].some(t => t === value);
      }
    },
    // String: KEY of SHORTCUT_SETS
    // Array<String>: VALUE of shortcuts item key
    // For Example:
    // 'A'
    // ['yesterday', 'this month']
    shortcuts: {
      required: false,
      default: 'A',
      validator(value) {
        if (typeof value === 'string') {
          return Object.keys(SHORTCUT_SETS).some(k => k === value);
        }
        if (Array.isArray(value)) {
          return value.every(t => typeof t === 'string');
        }
        return false;
      }
    }
  },

  computed: {
    date: {
      get() {
        return this.value;
      },
      set(value) {
        this.$emit('input', value);
      }
    },
    ...['minDate', 'maxDate'].reduce(
      (o, prop, i) => ({
        ...o,
        [prop]() {
          const value = Array.isArray(this.date) ? this.date[i] : this.date;
          const format = this.attrs['value-format'];
          if (typeof value === 'string' && format) {
            return parseDate(value, format);
          }
          return value;
        }
      }),
      {}
    ),
    pickerOptions() {
      const shortcutKeys = Array.isArray(this.shortcuts)
        ? this.shortcuts
        : SHORTCUT_SETS[this.shortcuts];
      return {
        shortcuts: filterShortcusts(
          getPickerShortcuts(this),
          shortcutKeys || []
        ),
        onPick: ({ minDate }) => {
          this.$pickDate = minDate;
        },
        disabledDate: time => {
          const { $pickDate } = this;
          const sec = t => t.getTime();
          const tt = sec(time);
          const nt = sec(new Date());

          // 不能选择未来时间
          if (tt > nt) return true;

          // 只选择了一个日期
          if ($pickDate) {
            // 限制手动选择时间不能超过31天
            const DAYS_RANGE = 31;
            const lower = subDays($pickDate, DAYS_RANGE);
            const upper = addDays($pickDate, DAYS_RANGE);
            if (tt < sec(lower)) return true;
            if (tt > sec(upper)) return true;
          }

          return false;
        }
      };
    },
    attrs() {
      const attrs = {
        pickerOptions: this.pickerOptions,
        ...this.$attrs,
        type: this.type
      };
      return excludeKeys(attrs, ['value']);
    }
  },

  watch: {
    // 如果范围起始值未设置，清除选择限制
    minDate(value) {
      if (!value) {
        this.$pickDate = null;
      }
    },
    // 如果范围结束值被设置，清除选择限制
    maxDate(value) {
      if (value) {
        this.$pickDate = null;
      }
    }
  }
};
</script>
